/*!
//      
//      \file	k2k_StateHandlerMlkC.c
//      
//      \author	Copyright 2010 Valentina Vuksic <vuksv1@bfh.ch>, Beat Liechti <liecb2@bfh.ch>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.
//
//      Abstract Data Type for the state handler of the state "MlkC" (Milk Coffee)
//      
//      \brief		CAS-EBX-03 Projektarbeit 
//      			Valentina Vuksic (VUKSV1), Beat Liechti (LIECB2)
//      			StateHandlerMlkC ADT for the k2k (Kaffee2000) application
//      \version	1.0
//      \date		2010-04-11
//      \remark		Last Modifications:
//      \remark  	v1.0, VUKSV1/LIECB2, 2010-04-11 Initial release
//
*/

#include <stdio.h>
#include <unistd.h>

#include "k2k_StateHandlerMlkC.h"
#include "k2k_AudioPlayer.h"

#define STATE_TEXT_1	"Making one milk coffee"
#define STATE_TEXT_2	"Making two milk coffee"

/* -- static function declarations -----------------------------------*/

static k2k_State k2k_StateHandlerMlkC_Handler(k2k_Event event, k2k_State previousState, k2k_StateHandlerData *handlerData);
static void k2k_StateHandlerMlkC_InitState(k2k_StateHandlerMlkC *stateHandlerMlkC, k2k_State previousState);
static k2k_State k2k_StateHandlerMlkC_ExitState(k2k_StateHandlerMlkC *stateHandlerMlkC, k2k_State nextState);
static void k2k_StateHandlerMlkC_MakeCoffee(k2k_StateHandlerMlkC *stateHandlerMlkC);
static void k2k_StateHandlerMlkC_SignalHandler(int sigNo, void *customData);

/* -- ADT functions --------------------------------------------------*/

/*!
//
// Creates StateHandlerMlkC ADT "object"
//
// \param stateMachine is the pointer to the StateMachine structure
// 
// \return pointer to the StateHandlerMlkC ADT
//
*/
k2k_StateHandlerMlkC* k2k_StateHandlerMlkC_Create(k2k_StateMachine *stateMachine)
{
	k2k_StateHandlerMlkC *stateHandlerMlkC = NULL;
	stateHandlerMlkC = malloc(sizeof(k2k_StateHandlerMlkC));
	if (stateHandlerMlkC != NULL)
	{
		/*! initialize structure */		
		stateHandlerMlkC->handler = k2k_StateHandlerMlkC_Handler;	
		stateHandlerMlkC->coffeeCount = 0;	
		stateHandlerMlkC->coffeeDoneCount = 0;
		stateHandlerMlkC->fillDegree = 0;	
		/*! setup signaling */
		stateHandlerMlkC->signal = k2k_Signal_Create(k2k_SIG_MLKC, k2k_StateHandlerMlkC_SignalHandler, stateHandlerMlkC);
		if (stateHandlerMlkC->signal == NULL)
		{
			stateHandlerMlkC->err = k2k_STATEHANDLER_ERR_CREATE_SIGNAL_FAILED;
		}
		else
		{
			/*! reset signal variable */
			stateHandlerMlkC->sigNo = 0;
			/*! create state handler data */
			stateHandlerMlkC->handlerData = k2k_StateHandlerData_Create(stateHandlerMlkC);		
			if (stateHandlerMlkC->handlerData == NULL)
			{
				stateHandlerMlkC->err = k2k_STATEHANDLER_ERR_CREATE_HANDLERDATA_FAILED;
			}
			else
			{		
				/*! add the state handler to the state machine */
				k2k_StateMachine_AddStateHandler(stateMachine, STATE_MLKC, stateHandlerMlkC->handler, stateHandlerMlkC->handlerData);
				/*! store reference to state machine */
				stateHandlerMlkC->stateMachine = stateMachine;
				/*! show icon */
				k2k_NanoX_ShowMlkCIcon(stateMachine->nanoX);
				/*! flag success */
				stateHandlerMlkC->err = k2k_STATEHANDLER_ERR_SUCCESS;
			}
		}
	}
	return stateHandlerMlkC;
}

/*!
//
// Releases StateHandlerMlkC ADT "object"
// 
// \param stateHandlerMlkC is the pointer to the StateHandlerMlkC structure 
// 
// \return nothing
//
*/
void k2k_StateHandlerMlkC_Release(k2k_StateHandlerMlkC *stateHandlerMlkC)
{
	if (stateHandlerMlkC != NULL)
	{	
		if (stateHandlerMlkC->signal != NULL)
		{
			/*! release signal ADT */
			k2k_Signal_Release(stateHandlerMlkC->signal);
			stateHandlerMlkC->signal = NULL;
		}
		if (stateHandlerMlkC->handlerData != NULL)
		{
			/*! release handler data */
			k2k_StateHandlerData_Release(stateHandlerMlkC->handlerData);
			stateHandlerMlkC->handlerData = NULL;
		}
		/*! release memory */
		free(stateHandlerMlkC);	
	}
}

/* -- static functions -----------------------------------------------*/

/*!
//
// Actual state handler for the state "MlkC"
//
// \param event describes the event that happened
// \param previousState describes the previous state
// \param handlerData points to a structure for custom data 
// 		  (typically containing a pointer the corresponding ADT struct)
//
// \return next state of the event machine
//
*/
static k2k_State k2k_StateHandlerMlkC_Handler(k2k_Event event, k2k_State previousState, k2k_StateHandlerData *handlerData)
{
	/*! get the state handler structure */
	k2k_StateHandlerMlkC *stateHandlerMlkC = handlerData->customData;
	
	/*! are we entering the state? */
	if (previousState == STATE_RDY1 || previousState == STATE_RDY2)
	{		
		/*! initialize state */
		k2k_StateHandlerMlkC_InitState(stateHandlerMlkC, previousState);
		/*! make a coffee */
		k2k_StateHandlerMlkC_MakeCoffee(stateHandlerMlkC);
	}
	
	/*! the default is to remain in the same state */	
	int nextState = STATE_MLKC;
	switch (event)
	{
		case EVENT_S1:
			/*! go into "Off" state */
			nextState = k2k_StateHandlerMlkC_ExitState(stateHandlerMlkC, STATE_OFF);
			break;		
		case EVENT_TIMER: 
			/*! check if signal has arrived */
			if (stateHandlerMlkC->sigNo != 0)
			{
				/*! disable signal handler */
				k2k_Signal_Disable(stateHandlerMlkC->signal);
				/*! count the coffe made */
				stateHandlerMlkC->coffeeDoneCount++;
				/*! count the coffe globally */	
				k2k_StateMachine_CountOneCoffee();				
				/*! make the second coffee or go into "Rdy1" state */
				if (stateHandlerMlkC->coffeeDoneCount < stateHandlerMlkC->coffeeCount)
				{	
					/*! make another coffee */
					k2k_StateHandlerMlkC_MakeCoffee(stateHandlerMlkC);
				}		
				else
				{
					/*! go into "Rdy1" state */
					nextState = k2k_StateHandlerMlkC_ExitState(stateHandlerMlkC, STATE_RDY1);
				}
			}
			else
			{
				/*
				stateHandlerMlkC->timerCount++;
				if (stateHandlerMlkC->timerCount > 50)
				{
					k2k_StateMachine_SetGrinderSpeed(stateHandlerMlkC->stateMachine, 0);
					k2k_StateMachine_SetMilkPumpAmount(stateHandlerMlkC->stateMachine, 0);
				}
				*/
				/*! blink the cups */
				k2k_NanoX_BlinkCups(stateHandlerMlkC->stateMachine->nanoX, stateHandlerMlkC->fillDegree, stateHandlerMlkC->coffeeCount, stateHandlerMlkC->coffeeDoneCount);
			}
			break;
		default:
			break;
	}	
	
	/*! update animation for activity */
	k2k_NanoX_UpdateMlkCAnimation(stateHandlerMlkC->stateMachine->nanoX, &stateHandlerMlkC->fillDegree); 
	
	return nextState;
}

/*!
//
// Initialize the state, called when enetering from another state
//
// \param stateHandlerMlkC is the pointer to the StateHandlerMlkC structure 
// \param previousState describes the previous state
//
// \return nothing
//
*/
static void k2k_StateHandlerMlkC_InitState(k2k_StateHandlerMlkC *stateHandlerMlkC, k2k_State previousState)
{
	/*! initialization depends on what the previous state was */
	if (previousState == STATE_RDY1)
	{
		/*! make one milk coffee  */
		stateHandlerMlkC->coffeeCount = 1;
		printf("Making one milk coffee...\n");
		/*! set state text */
		k2k_NanoX_ShowStateText(stateHandlerMlkC->stateMachine->nanoX, STATE_TEXT_1);
	}
	else
	{
		/*! make two milk coffees */		
		stateHandlerMlkC->coffeeCount = 2;
		printf("Making two milk coffees...\n");
		/*! set state text */
		k2k_NanoX_ShowStateText(stateHandlerMlkC->stateMachine->nanoX, STATE_TEXT_2);
	}
	/*! reset coffee count */
	stateHandlerMlkC->coffeeDoneCount = 0;
	/*! start with empty animation */
	stateHandlerMlkC->fillDegree = 0;
}

/*!
//
// Leave the state, called when conditions are met to exit the state 
//
// \param stateHandlerMlkC is the pointer to the StateHandlerMlkC structure 
// \param nextState describes the next state
//
// \return nothing
//
*/
static k2k_State k2k_StateHandlerMlkC_ExitState(k2k_StateHandlerMlkC *stateHandlerMlkC, k2k_State nextState)
{
	k2k_StateMachine_SetGrinderSpeed(stateHandlerMlkC->stateMachine, 0);
	k2k_StateMachine_SetMilkPumpAmount(stateHandlerMlkC->stateMachine, 0);
	/*! disable signal handler */
	k2k_Signal_Disable(stateHandlerMlkC->signal);
	/*! clear animation by setting fill degree to zero */
	stateHandlerMlkC->fillDegree = 0;
	/*! go into "Off" state */
	return nextState;
}

/*!
//
// "Make" a coffee
//
// \param stateHandlerMlkC is the pointer to the StateHandlerMlkC structure 
// \param previousState describes the previous state
//
// \return nothing
//
*/
static void k2k_StateHandlerMlkC_MakeCoffee(k2k_StateHandlerMlkC *stateHandlerMlkC)
{
	stateHandlerMlkC->timerCount = 0;
	k2k_StateMachine_SetGrinderSpeed(stateHandlerMlkC->stateMachine, 100);
	k2k_StateMachine_SetMilkPumpAmount(stateHandlerMlkC->stateMachine, 255);
	/*! reset signal variable */
	stateHandlerMlkC->sigNo = 0;
	/*! enable signal handler */
	k2k_Signal_Enable(stateHandlerMlkC->signal);
	/*! play clip ==> "making the coffee" */
	k2k_AudioPlaySignal(CLIP_MLKC, stateHandlerMlkC->signal);
}

/*!
//
// Signal handler for the state "MlkC" to synchronize with child process
//
// \param sigNo describes the signal sent by the child process
// \param customData points to custom data (typically the corresponding ADT struct)
//
// \return nothing
//
*/
static void k2k_StateHandlerMlkC_SignalHandler(int sigNo, void *customData)
{	
	k2k_StateHandlerMlkC *stateHandlerMlkC = customData;
	stateHandlerMlkC->sigNo = sigNo;
	printf("k2k_StateHandlerMlkC_SignalHandler: received 0x%08X\n", sigNo);
}
